package org.iabako.server.serviceimpl.business;

import org.apache.log4j.Logger;
import org.iabako.server.dao.business.NotificationDAO;
import org.iabako.server.dao.user.EnterpriseDAO;
import org.iabako.server.serviceimpl.security.SessionUser;
import org.iabako.shared.entity.business.Notification;
import org.iabako.shared.entity.business.NotificationConfig;
import org.iabako.shared.entity.enumeration.NotificationType;
import org.iabako.shared.entity.user.Enterprise;
import org.iabako.shared.entity.user.User;
import org.iabako.shared.iservice.business.NotificationService;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * Created by jose on 13/03/15.
 */

@org.springframework.stereotype.Service("notificationService")
public class NotificationServiceImpl implements NotificationService {

    static Logger log = Logger.getLogger(NotificationServiceImpl.class.getName());

    @Autowired
    NotificationDAO notificationDAO;
    @Autowired
    EnterpriseDAO enterpriseDAO;
    @Autowired
    SessionUser sessionUser;

    public List<Notification> calculateNotifications(Enterprise enterprise) {
        return notificationDAO.calculateNotifications(enterprise, null);
    }

    public List<Notification> getNotifications(Enterprise enterprise) {
        return notificationDAO.getNotifications(enterprise);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public List<Notification> getUserSessionNotifications() {
        return getNotificationsByUser(sessionUser.getUser());
    }

    protected User getSessionUser() {
        try {
            return sessionUser != null ? sessionUser.getUser() : null;
        } catch (BeanCreationException exception) {
            return null;
        }
    }


    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public List<Notification> getNotificationsByUser(User user) {
        //We must charge notifications from DB in this transaction to
        // 1. Reach lazy collections
        // 2. Take into account notifications of others users of the same enterprise
        List<Notification> notifications = notificationDAO.getNotifications(user.getEnterprise());

        if (getSessionUser() == user && user.getEnterprise().getUsersNumber() > 1) {
            //If this method is accessed by UI (user transaction) and by a multi-user Enterprise,
            //Then, we must reset NotificationList in sessionUser object
            sessionUser.getUser().getEnterprise().setNotificationList(notifications);
        }

        List<Notification> toDelete = new ArrayList<Notification>();
        for (Notification notification : notifications) {
            if (!notification.getHiddenUsers().isEmpty()) {
                Notification notificationFromDB = notificationDAO.getById(notification.getId());
                if (notificationFromDB.getHiddenUsers().contains(user)) {
                    toDelete.add(notification);
                }
            }
        }
        notifications.removeAll(toDelete);
        return notifications;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public List<Notification> calculateNewNotifications(NotificationType[] notificationType) {
        Enterprise enterprise = sessionUser.getUser().getEnterprise();

        for (Enterprise e : enterprise.getAllRelatedEnterprises()) {
            calculateNewNotifications(notificationType, e);
        }

        return getUserSessionNotifications();
    }

    private void calculateNewNotifications(NotificationType[] notificationType, Enterprise enterprise) {

        List<NotificationType> notificationsTypeList = notificationType != null ? Arrays.asList(notificationType) : new ArrayList<NotificationType>();

        List<Notification> notificationDB = getNotifications(enterprise);
        List<Notification> notificationsToAdd = notificationDAO.calculateNotifications(enterprise, notificationType);

        //Conserve hidden users configuration if Notification has not changed
        for (Notification newNotif : notificationsToAdd) {
            for (Notification oldNotif : notificationDB) {
                if (oldNotif.getType() == newNotif.getType()
                        && oldNotif.getCardinal() == newNotif.getCardinal()) {
                    newNotif.getHiddenUsers().addAll(oldNotif.getHiddenUsers());
                    break;
                }
            }
        }

        List<Notification> toDelete = new ArrayList<Notification>();

        for (Notification notification : notificationDB) {
            if (notificationsTypeList.contains(notification.getType())) {
                toDelete.add(notification);
            }
        }
        if (!toDelete.isEmpty()) {
            notificationDB.removeAll(toDelete);
        }
        if (!notificationsToAdd.isEmpty()) {
            notificationDB.addAll(notificationsToAdd);
        }

        enterprise.setNotificationList(notificationDB);
        enterprise.setLastNotificationCalculation(new Date());
        enterpriseDAO.merge(enterprise);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void hideNotification(Notification notification) {
        //We search notification by type and no by ID because they could be different in server and client side
        Notification notificationFromDB = notificationDAO.getByNotificationType(notification.getType());
        if (notificationFromDB != null) {
            notificationFromDB.getHiddenUsers().add(sessionUser.getUser());
            notificationDAO.merge(notificationFromDB);
        } else {
            log.warn("Notification not found in server side (ID=" + notification.getId() + ", TYPE=" + notification.getType() + "). How could this happen ?");
        }
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void saveNotificationConfig(NotificationConfig entity) {
        for (Enterprise e : sessionUser.getUser().getEnterprise().getAllRelatedEnterprises()) {
            if (!e.equals(sessionUser.getUser().getEnterprise())) {
                e.setNotificationConfig(entity.customClone());
                enterpriseDAO.merge(e);
            }
        }
        sessionUser.getUser().getEnterprise().setNotificationConfig(enterpriseDAO.saveNotificationConfig(entity));
    }
}
